﻿using Castle.DynamicProxy;
using Newtonsoft.Json;
using Syspetro.Core.Extensions;
using Syspetro.Core.SysInterface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace Syspetro.Core.AopService
{
    public static class AopExtend
    {
        public static object AOP(this object t, Type interfaceType)
        {
            ProxyGenerator generator = new();
            IOCInterceptor interceptor = new();
            t = generator.CreateInterfaceProxyWithTarget(interfaceType, t, interceptor);
            return t;
        }
        public static T AOP<T>(this object t)
        {
            ProxyGenerator generator = new();
            IOCInterceptor interceptor = new();
            t = generator.CreateInterfaceProxyWithTarget(typeof(T), t, interceptor);
            return (T)t;
        }

        public static T AopLock<T>(this object t, string key = null, TimeSpan? expiry = default)
        {
            ProxyGenerator generator = new();
            LockIOCInterceptor interceptor = new(key, expiry);
            t = generator.CreateInterfaceProxyWithTarget(typeof(T), t, interceptor);
            return (T)t;
        }

        public static T AopCache<T, Result>(this object t, string key = null, TimeSpan? expiry = default)
        {
            ProxyGenerator generator = new();
            CacheIOCInterceptor<Result> interceptor = new(key, expiry);
            t = generator.CreateInterfaceProxyWithTarget(typeof(T), t, interceptor);
            return (T)t;
        }
    }
    /// <summary>
    /// 切面逻辑
    /// </summary>
    public class IOCInterceptor : StandardInterceptor
    {
        IEnumerable<BaseInterceptorAttribute> attributes;
        Action action;
        protected override void PreProceed(IInvocation invocation)
        {
            var method = invocation.Method;
            if (method.IsDefined(typeof(BaseInterceptorAttribute), true))
            {
                attributes = method.GetCustomAttributes<BaseInterceptorAttribute>().ToArray().Reverse();
            }
            action = () => base.PerformProceed(invocation);
            foreach (var attribute in attributes)
            {
                action = attribute.DoIn(invocation, action);
            }
        }
        /// <summary>
        /// 拦截的方法返回时调用的拦截器
        /// </summary>
        /// <param name="invocation"></param>
        protected override void PerformProceed(IInvocation invocation)
        {
            foreach (var attribute in attributes)
            {
                action = attribute.Do(invocation, action);
            }
            action.Invoke();
        }
        protected override void PostProceed(IInvocation invocation)
        {
            foreach (var attribute in attributes)
            {
                action = attribute.DoOut(invocation, action);
            }
        }
    }
    /// <summary>
    /// 锁
    /// </summary>
    public class LockIOCInterceptor : StandardInterceptor
    {
        readonly ILock locker;
        private string key;
        readonly TimeSpan expiry;
        private bool lockerbl = false;
        public LockIOCInterceptor(string key = null, TimeSpan? expiry = default)
        {
            this.key = key;
            this.expiry = expiry ?? new TimeSpan(0, 0, 1);
            locker = InternalApp.ServicesProvider.GetService(typeof(ILock)) as ILock;
        }
        protected override void PreProceed(IInvocation invocation)
        {
            this.key = key ?? invocation.Method.Name;
            lockerbl = Task.Run(() =>
            {
                return locker.GetAwait(key, expiry);
            }).Result;
        }
        /// <summary>
        /// 拦截的方法返回时调用的拦截器
        /// </summary>
        /// <param name="invocation"></param>
        protected override void PerformProceed(IInvocation invocation)
        {
            if (lockerbl)
            {
                if (!locker.Set(key, expiry))
                    Console.WriteLine($"加{key}锁失败并继续执行");
                base.PerformProceed(invocation);
            }
            else
            {
                throw new($"获取{key}锁失败");
            }
        }
        protected override void PostProceed(IInvocation invocation)
        {
            if (locker.Remove(key))
                Console.WriteLine($"释放{key}锁");
        }
    }
    /// <summary>
    /// 缓存
    /// </summary>
    public class CacheIOCInterceptor<Result> : StandardInterceptor
    {
        readonly ICache cacher;
        private string key;
        readonly TimeSpan expiry;
        private string jsondate;
        public CacheIOCInterceptor(string key = null, TimeSpan? expiry = default)
        {
            this.key = key;
            this.expiry = expiry ?? new TimeSpan(0, 0, 1);
            cacher = InternalApp.ServicesProvider.GetService(typeof(ICache)) as ICache;
        }
        protected override void PreProceed(IInvocation invocation)
        {
            if (invocation.Method.ReturnType.BaseType == typeof(Task))
                this.key = key + invocation.Method.ReturnType.ToString().Replace("System.Threading.Tasks.Task", "");
            else
                this.key = key + invocation.Method.ReturnType.Name;
            jsondate = cacher.Get(key);
        }
        protected override void PerformProceed(IInvocation invocation)
        {
            if (string.IsNullOrEmpty(jsondate))
            {
                base.PerformProceed(invocation);
            }
            else
            {
                if (invocation.Method.ReturnType.BaseType == typeof(Task))
                {
                    Task<Result> result = Task.Run(() => { return JsonConvert.DeserializeObject<Result>(jsondate); });
                    invocation.ReturnValue = result;
                }
                else
                {
                    var type = invocation.Method.ReturnType;
                    invocation.ReturnValue = JsonConvert.DeserializeObject(jsondate, type);
                }
            }
        }
        protected override void PostProceed(IInvocation invocation)
        {
            if (string.IsNullOrEmpty(jsondate))
            {
                if (invocation.Method.ReturnType.BaseType == typeof(Task))
                {
                    invocation.Proceed();
                    var task = (Task<Result>)invocation.ReturnValue;
                    Result result = task.Result;
                    cacher.Set(key, result.ToJson(), expiry);
                }
                else
                    cacher.Set(key, invocation.ReturnValue.ToJson(), expiry);
            }
        }
    }
}
